package com.my.study.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.my.study.enums.BusinessExceptionEnum;
import com.my.study.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestTemplate;

import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

@Slf4j
public class HttpClientUtil {

    private static final int readTimeout = 60000;

    private static final int connectTimeout = 1200000;

    private static final String HTTP = "http";

    private static final String HTTPS = "https";

    private static RestTemplate restTemplate;

    private HttpClientUtil() {
    }

    static {
        restTemplate = createRestTemplate(readTimeout, connectTimeout);
    }

    /**
     * HTTPS\HTTP(支持加载证书,将证书添加到jdk中)
     * 证书获取:服务方提供,或者服务方网站下载(证书与域名绑定)
     * 进入:%JAVA_HOME%jre\lib\security,打开cmd
     * keytool -importkeystore -srckeystore $JAVA_HOME/bin/cert.p12 -srcstoretype PKCS12 -destkeystore $JAVA_HOME/jre/lib/security/cacerts -deststoretype JKS
     * #输入命查看全部证书信息
     * keytool -list -v -keystore $JAVA_HOME/jre/lib/security/cacerts | less
     * #精确查找导入的证书
     * keytool -list -v -keystore $JAVA_HOME/jre/lib/security/cacerts | grep 四川
     * 参考地址:https://blog.csdn.net/m0_62160083/article/details/132166099
     *
     * @param readTimeout
     * @param connectTimeout
     * @return
     */
    public static RestTemplate createRestTemplate(int readTimeout, int connectTimeout) {
        // 加载连接工厂
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register(HTTP, PlainConnectionSocketFactory.INSTANCE)
                .register(HTTPS, SSLConnectionSocketFactory.getSystemSocketFactory()).build(); //加载jdk路径证书
        // 连接池
        PoolingHttpClientConnectionManager clientConnectionManager = new PoolingHttpClientConnectionManager(registry);
        // 设置客户端读取时间
        SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(readTimeout).build();
        clientConnectionManager.setDefaultSocketConfig(socketConfig);
        // 设置连接超时间
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(readTimeout).setConnectTimeout(connectTimeout).build();
        CloseableHttpClient httpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig)
                .setConnectionManager(clientConnectionManager).build();
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
        // 创建RestTemplate
        RestTemplate restTemplate = new RestTemplate(factory);
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
        return restTemplate;
    }

    /**
     * Get发送简单请求，不含body
     *
     * @param url
     * @param urlParam
     * @return
     */
    public static String doGet(String url, Map<String, String> urlParam) {
        return doGet(url, urlParam, null, new ParameterizedTypeReference<String>() {
        });
    }

    public static JSONObject doGet(String url, Map<String, String> urlParam, Map<String, String> header) {
        return doGet(url, urlParam, header, new ParameterizedTypeReference<JSONObject>() {
        });
    }

    public static <T> T doGet(String url, Map<String, String> urlParam, Map<String, String> header,
                              ParameterizedTypeReference<T> responseType) {
        try {
            // url参数拼接
            url = handleUrlParam(url, urlParam);

            //填充属性到请求头
            HttpHeaders httpHeaders = new HttpHeaders();
            fillParamsToHttpHeader(header, httpHeaders);
            HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(null, httpHeaders);

            // 发送请求
            ResponseEntity<T> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, responseType);
            return responseEntity.getBody();
        } catch (Exception exception) {
            handlerException(exception);
            log.error("Get request failed" + exception);
            throw BusinessException.fail(BusinessExceptionEnum.FAIL.code, "get 请求失败！");
        }
    }

    /**
     * POST请求
     *
     * @param url
     * @param body
     * @return
     */
    public static JSONObject doPost(String url, Map<String, Object> body) {
        return doPost(url, null, body, new ParameterizedTypeReference<JSONObject>() {
        });
    }

    public static String doPost(String url, Map<String, String> head, Map<String, Object> body) {
        return doPost(url, head, body, new ParameterizedTypeReference<String>() {
        });
    }

    public static <T> T doPost(String url, Map<String, String> head, Object body,
                               ParameterizedTypeReference<T> responseType) {

        try {
            HttpHeaders httpHeaders = new HttpHeaders();
            // 设置请求内容为json
            httpHeaders.setContentType(MediaType.APPLICATION_JSON);
            // 添加头部信息
            fillParamsToHttpHeader(head, httpHeaders);
            // 创建请求实体
            HttpEntity<Object> httpEntity = new HttpEntity<>(JSON.toJSONString(body), httpHeaders);
            // 执行请求
            ResponseEntity<T> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, responseType);
            return responseEntity.getBody();
        } catch (Exception exception) {
            handlerException(exception);
            log.error("Post request failed" + exception);
            throw BusinessException.fail(BusinessExceptionEnum.FAIL.code, "Post 请求失败！");
        }
    }

    /**
     * 异常处理
     *
     * @param exception
     */
    private static void handlerException(Exception exception) {
        if (exception instanceof HttpServerErrorException) {
            HttpServerErrorException serverErrorException = (HttpServerErrorException) exception;
            JSONObject jsonObject = JsonUtil.jsonToObject(serverErrorException.getResponseBodyAsString(), JSONObject.class);
            log.error("Get request failed" + serverErrorException);
            throw BusinessException.fail(jsonObject.getString("resultCode"), jsonObject.getString("resultMsg"));
        }
    }

    /**
     * url参数拼接
     *
     * @param url
     * @param urlParam
     * @return
     */
    private static String handleUrlParam(String url, Map<String, String> urlParam) {
        // 判空
        if (urlParam == null || urlParam.isEmpty()) {
            return url;
        }
        // url参数拼接
        Iterator<Map.Entry<String, String>> iterator = urlParam.entrySet().iterator();
        StringBuilder urlBuilder = new StringBuilder(url);
        urlBuilder.append("?");
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            urlBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
        }
        urlBuilder.deleteCharAt(urlBuilder.length() - 1);
        return String.valueOf(urlBuilder);
    }

    /**
     * 填充参数到请求头
     *
     * @param heads
     * @param headers
     */
    public static void fillParamsToHttpHeader(Map<String, String> heads, HttpHeaders headers) {
        // 判断空
        if (Objects.isNull(heads)) {
            return;
        }
        // 头部填充
        Set<String> keySet = heads.keySet();
        for (String s : keySet) {
            if (headers.get(s) == null) {
                headers.add(s, "");
            } else {
                headers.add(s, String.valueOf(heads.get(s)));
            }
        }
    }
}
